package com.yxy.web.controller.system;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.yxy.common.constant.CacheConstants;
import com.yxy.common.constant.Constants;
import com.yxy.common.core.domain.AjaxResult;
import com.yxy.common.core.domain.entity.SysUser;
import com.yxy.common.core.domain.model.LoginUser;
import com.yxy.common.utils.DateUtils;
import com.yxy.common.utils.MessageUtils;
import com.yxy.common.utils.ip.IpUtils;
import com.yxy.framework.manager.AsyncManager;
import com.yxy.framework.manager.factory.AsyncFactory;
import com.yxy.framework.web.service.SysLoginService;
import com.yxy.framework.web.service.SysPermissionService;
import com.yxy.framework.web.service.TokenService;
import com.yxy.system.mapper.SysUserMapper;
import com.yxy.system.service.ISysUserService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
public class SSOController {

    private static final Logger log = LoggerFactory.getLogger(SSOController.class);
    @Autowired
    private SysLoginService loginService;


    @Value("${sso.url}")
        private   String ssoUrl;
    @Value("${sso.client_id}")
    public  String client_id;

    @Value("${sso.client_secret}")
    public  String client_secret;

    @Value("${sso.redirect_url}")
    public  String redirect_url;
    @Resource
    SysUserMapper sysUserMapper;
    @Autowired
    private TokenService tokenService;

    @Autowired
    private ISysUserService userService;

    @Autowired
    private SysPermissionService permissionService;

    @Autowired
    public StringRedisTemplate redisTemplate;

    /**
     * 登录方法
     *
     * @return 结果
     */
    @GetMapping("/sso/login")
    public AjaxResult ssologin(HttpServletRequest request, HttpServletResponse response, Model model) throws IOException {
        AjaxResult ajax = AjaxResult.success();
        try {
            String code = request.getParameter("code");
            String redirect_uri = request.getParameter("redirect_uri");
            log.info("oss come in.... code:" + code);

            // 1. 检查code
            if (StrUtil.isBlank(code)) {
                return AjaxResult.error("100", "授权服务器异常，无授权临时code");
            }

            // 2. 获取token
            Map tokenMap = getAccessToken(code);
            if (tokenMap == null) {
                return AjaxResult.error("100", "CODE换取令牌失败");
            }
            if (!tokenMap.get("retCode").equals("200")) {
                return AjaxResult.error("100", tokenMap.get("errorMsg"));
            }
            // 3. 获取用户信息
            Map userMap = getUsernameByToken(tokenMap);
            if (userMap == null) {  // 修正了这里的判断条件
                return AjaxResult.error("100", "授权获取用户信息失败");
            }
            if (!userMap.get("retCode").equals("200")) {
                return AjaxResult.error("100", userMap.get("errorMsg"));
            }

            // 4. 检查用户是否存在
            Map<String, Object> dataMap = (Map<String, Object>) userMap.get("data");
            SysUser sysUser = sysUserMapper.selectUserByUserName(dataMap.get("accountName").toString());
            if (sysUser == null) {
                log.info(dataMap.get("accountName").toString() + "查无此账户，此账户暂未在该系统初始化账户");
                return AjaxResult.error("100", "查无此账户，此账户暂未在该系统初始化账户，请联系管理员进行账户注册授权");
            }
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(dataMap.get("accountName").toString(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
//            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            // 创建模拟的Authentication对象
            // 2. 构建权限集合
            Set<String> permissions = permissionService.getMenuPermission(sysUser);
            Set<String> roles = permissionService.getRolePermission(sysUser);

            // 3. 创建GrantedAuthority集合
            List<GrantedAuthority> authorities = new ArrayList<>();
            // 添加权限
            permissions.forEach(perm ->
                    authorities.add(new SimpleGrantedAuthority(perm)));

            // 添加角色（通常需要ROLE_前缀）
            roles.forEach(role ->
                    authorities.add(new SimpleGrantedAuthority("ROLE_" + role)));
            LoginUser loginUser = new LoginUser();
            loginUser.setUserId(sysUser.getUserId());
            loginUser.setDeptId(sysUser.getDeptId());
            loginUser.setUser(sysUser);
            loginUser.setPermissions(permissions);
            loginUser.setLoginTime(DateUtil.current());
            Authentication authentication = new UsernamePasswordAuthenticationToken(
                    loginUser,
                    null,
                    authorities
            );
            // 6. 如果需要，可以将authentication存入SecurityContext
            SecurityContextHolder.getContext().setAuthentication(authentication);
            recordLoginInfo(loginUser.getUserId());
            // 生成token
            String token = tokenService.createToken(loginUser);
            log.info("登录的tokenService.createToken(loginUser) token:" + loginUser.getToken());
            ajax.put(Constants.TOKEN, token);
            String redirectUrl;
            if (StrUtil.isNotBlank(redirect_uri)) {
                redirectUrl= redirect_url+"/sso-callback?token=" + URLEncoder.encode(token, "UTF-8")+"&redirect_uri="+redirect_uri;
            }else {
                 redirectUrl = redirect_url+"/sso-callback?token=" + URLEncoder.encode(token, "UTF-8");
            }
            Map reMap= (Map) tokenMap.get("data");
            String accessToken= reMap.get("access_token").toString();
            // 1. 检查该用户是否已有登录记录
            String userTokenKey = "user_token:" + sysUser.getUserId();
            String josnData = redisTemplate.opsForValue().get(userTokenKey);

            // 2. 如果存在旧token，清理相关key
            if (StrUtil.isNotBlank(josnData)) {
                Map<String, Object> josnMap = JSON.parseObject(josnData);
                // 删除旧的token记录
                redisTemplate.delete(userTokenKey);
                redisTemplate.delete(CacheConstants.LOGIN_TOKEN_KEY + josnMap.get("user_token"));
            }
            Map map=new HashMap();
            map.put("access_token",accessToken);
            map.put("user_token",loginUser.getToken());
            map.put("user_id",sysUser.getUserId());
            map.put("user_name",sysUser.getUserName());
            // 3. 生成新token后，存储用户-token关联
            redisTemplate.opsForValue().set(
                    userTokenKey,
                    JSONUtil.toJsonStr(map),
                    1, TimeUnit.HOURS  // 与token同生命周期
            );
            response.sendRedirect(redirectUrl);
            return ajax;
        }catch (Exception ex){
            log.info("ssoLogin error :"+ex);
            String redirectUrl = redirect_url+"/sso-callback?msg=" + URLEncoder.encode(ex.getMessage(), "UTF-8");
            response.sendRedirect(redirectUrl);
            return AjaxResult.error("100",ex.getMessage());
        }
    }
    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId)
    {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr());
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }

    private Map getUsernameByToken(Map map) throws IOException {
        if (null == map) {
            throw new IOException("获取token信息失败");
        }
        Map reMap= (Map) map.get("data");
        String url = ssoUrl;
        StringBuffer sb = new StringBuffer();
        log.info("获取token 地址：" + sb.toString());
        Map<String, String> headersMap = new HashMap();
        headersMap.put("Accept", "application/json");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("client_id", client_id);
        jsonObject.put("access_token", reMap.get("access_token"));
        log.info("获取token参数" + JSONUtil.toJsonStr(jsonObject));
        sb.append(url).append("/oauth2/getUserInfo?").append("client_id=").append(client_id);
        HttpResponse response = HttpUtil.createPost(sb.toString()).addHeaders(headersMap)
                .body(JSONUtil.toJsonStr(jsonObject)).execute();
        if (!response.isOk()) {
            throw new IOException("Unexpected code " + response);
        }
        String body = response.body();
        log.info("获取个人信息，返回数据结果：" + body);
        if (!response.isOk()) {
            throw new IOException("Unexpected code " + response);
        }
        if (!ObjectUtil.isNotNull(body)) {
            throw new IOException("返回报文为空");
        }
        return JSONUtil.toBean(body, Map.class);
    }


    private  Map  getAccessToken(String code) throws IOException {
        StringBuffer sb = new StringBuffer();
        sb.append(ssoUrl).append("/oauth2/getToken");
        log.info("获取code 地址：" + sb.toString());
        Map<String, String> headersMap = new HashMap();
        headersMap.put("Accept", "application/json");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("client_id", client_id);
        jsonObject.put("client_secret", client_secret);
        jsonObject.put("code", code);
        jsonObject.put("grant_type", "authorization_code");
        log.info("获取token参数" + JSONUtil.toJsonStr(jsonObject));
        HttpResponse response = HttpUtil.createPost(sb.toString()).addHeaders(headersMap)
                .body(JSONUtil.toJsonStr(jsonObject)).execute();
        if (!response.isOk()) {
            throw new IOException("Unexpected code " + response);
        }
        String body = response.body();
        log.info("code获取token,code:" + code + " 返回数据结果：" + body);
        if (!response.isOk()) {
            throw new IOException("Unexpected code " + response);
        }
        if (StrUtil.isBlank(body)) {
            throw new IOException("返回报文为空");
        }
        return JSONUtil.toBean(body, Map.class);
    }
}
